// ==UserScript==
// @name         豆包Pro
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  增加代码块下载并用trae打开方式
// @author       Your Name
// @match        https://www.doubao.com/*
// @grant        GM_notification
// @grant        GM_log
// ==/UserScript==

(function () {
  "use strict";
  // --------------------------
  // 全局变量
  // --------------------------
  var reqParams = "";
  var articleList = [];

  // [增加Item项] 1. 定义要新增的li元素的HTML结构
  const newLiHtml = `
        <li role="menuitem" tabindex="-1" aria-disabled="false" class="semi-dropdown-item" data-testid="canvas-download-trae-cn-btn">
            <div>
                <div class="download-menu-item-title-Ddo5kR">
                    <span role="img" class="semi-icon semi-icon-default mr-8 text-16">
                        <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24">
                            <path fill="currentColor" d="M14.152 21.5q-.4 0-.705-.27a1.18 1.18 0 0 1-.368-.684 32 32 0 0 0-.58-2.687q-.294-1.079-.725-1.763a3.3 3.3 0 0 0-1.105-1.1q-.684-.414-1.746-.664a31 31 0 0 0-2.62-.477 1.2 1.2 0 0 1-.716-.363q-.273-.3-.273-.716 0-.415.284-.715.285-.312.715-.363a26 26 0 0 0 2.641-.415q1.074-.24 1.768-.654a3.4 3.4 0 0 0 1.126-1.12q.442-.705.715-1.794.273-1.1.515-2.697.064-.426.358-.685.306-.27.716-.27t.705.27q.305.26.378.685.285 1.597.569 2.686.294 1.08.725 1.764.432.683 1.116 1.1.683.414 1.736.663a30 30 0 0 0 2.62.467q.442.072.715.373.285.3.284.706 0 .425-.284.736-.284.3-.726.342-1.557.177-2.63.425-1.074.24-1.778.654a3.3 3.3 0 0 0-1.126 1.12q-.432.706-.705 1.795a30 30 0 0 0-.516 2.697 1.1 1.1 0 0 1-.368.684q-.294.27-.715.27M6.366 9.078q-.525 0-.589-.54a18 18 0 0 0-.252-1.565q-.127-.57-.421-.861-.295-.301-.884-.446-.59-.146-1.641-.29Q2 5.291 2 4.793t.516-.58a24 24 0 0 0 1.683-.343q.61-.156.905-.435.294-.291.42-.84.127-.561.253-1.546.063-.55.59-.55.515 0 .589.529.126 1.037.252 1.618.137.581.431.871.295.281.895.415.6.136 1.662.28.23.022.379.187.147.165.147.394 0 .488-.505.581-1.083.177-1.694.343-.6.155-.894.446-.295.279-.421.84T6.956 8.56a.58.58 0 0 1-.2.373.58.58 0 0 1-.39.145"></path>
                        </svg>
                    </span>
                    <span>使用 Trae国际版 打开</span>
                </div>
                <span class="download-menu-item-tip-I2xE6S">下载后使用 AI 原生 IDE 继续开发</span>
            </div>
        </li>
    `;

  // --------------------------
  // 监听配置 -- 监听数据API请求
  // --------------------------
  const config = {
    // 需要监听的URL规则列表
    watchRules: [
      {
        // URL包含此字符串的请求会被监听
        // 监听artifact请求
        urlContains: "/artifact/search",
        // 匹配时执行的操作
        onMatch: function (requestInfo) {
          if (
            typeof requestInfo.response === "object" &&
            requestInfo.response !== null &&
            requestInfo.response.code === 0
          ) {
            console.log("[监听器] 匹配到数据API请求，请求成功:", requestInfo);
            // 保存artifacts信息到localStorage
            saveArtifactsToLocalStorage(requestInfo.response.data);
            articleList = requestInfo.response.data;
          } else {
            console.log("[监听器] 匹配到数据API请求，请求失败:", requestInfo);
          }

          // 这里可以添加你的自定义处理逻辑
          // 比如分析响应数据、修改数据、存储到本地等
        },
      },
      {
        // 监听基础请求参数
        urlContains: "/setting/get",
        // 匹配时执行的操作
        onMatch: function (requestInfo) {
          console.log("[监听器] 匹配到setting/get请求:", requestInfo);
          // 获取URL参数
          // 获取到的url并不完整
          const url = new URL(requestInfo.url, window.location.href);
          const params = url.searchParams;

          // 将URL参数转换为对象，过滤掉msToken和a_bogus参数
          const urlParams = {};
          for (const [key, value] of params.entries()) {
            // 跳过msToken和a_bogus参数
            if (key !== "msToken" && key !== "a_bogus") {
              urlParams[key] = value;
            }
          }
          console.log("[监听器] setting/get请求的URL参数:", urlParams);
          reqParams = objToParams(urlParams);
        },
      },
    ],

    // 是否在控制台显示所有请求（即使不匹配规则）
    logAllRequests: false,
  };

  // --------------------------
  // 工具函数
  // --------------------------

  /**
   * 检查URL是否匹配任何规则
   */
  function checkUrlMatchesRules(url) {
    return config.watchRules.filter((rule) => url.includes(rule.urlContains));
  }

  /**
   * 处理请求信息并执行匹配的规则
   */
  function processRequest(requestInfo) {
    // 检查是否匹配任何规则
    const matchingRules = checkUrlMatchesRules(requestInfo.url);

    // 执行所有匹配规则的操作
    matchingRules.forEach((rule) => {
      try {
        rule.onMatch(requestInfo);
      } catch (error) {
        console.error(`[监听器] 执行规则时出错:`, error);
      }
    });

    // 如果配置了记录所有请求，则在控制台显示
    if (config.logAllRequests || matchingRules.length > 0) {
      console.log(`[监听器] ${requestInfo.type} 请求:`, requestInfo);
    }
  }

  /**
   * [增加Item项]
   * 2. 为目标semi-dropdown元素添加新item
   */
  function addTraeMenuItem(dropdownEl) {
    // 找到dropdown内部的ul列表（根据DOM结构，ul的class为semi-dropdown-menu）
    const targetUl = dropdownEl.querySelector("ul.semi-dropdown-menu");
    if (!targetUl) {
      console.log("未找到semi-dropdown内的ul列表，跳过");
      return;
    }

    // 检查是否已存在相同data-testid的li项（避免重复添加）
    const existingLi = targetUl.querySelector(
      'li[data-testid="canvas-download-trae-cn-btn"]'
    );
    if (existingLi) {
      console.log('已存在"使用Trae国际版打开"项，无需重复添加');
      return;
    }

    // 创建临时容器，将HTML字符串转为DOM元素
    const tempDiv = document.createElement("div");
    tempDiv.innerHTML = newLiHtml.trim(); // 去除首尾空格，避免多余文本节点
    const newLi = tempDiv.firstElementChild; // 获取li元素

    // 将新li添加到ul列表中（可根据需求调整位置，如prepend添加到开头）
    targetUl.appendChild(newLi);

    // 为新添加的li元素添加点击事件，点击后打开百度
    newLi.addEventListener("click", function () {
      //   window.open("https://www.baidu.com", "_blank");
      if (reqParams) {
        console.log("获取信息:", articleList);
        getResourceDownloadUrl(reqParams, articleList)
          .then((result) => {
            let res_link = result["data"]["url"];
            console.log("[监听器] 匹配到数据API请求，请求成功:", res_link);
            // 解决转义
            let replaceLink = res_link.replace(/\u0026/g, "&");
            // 编码成安全字符串
            let encodeLink = encodeURIComponent(replaceLink);
            let doubleEncodeLink = encodeURIComponent(encodeLink);
            let downloadLink =
              "trae://trae.ai-ide/import?contentType=gzip&source=doubao&shouldOpenRootFolder=true&url=" +
              doubleEncodeLink;
            console.log("生成下载链接", downloadLink);

            // 打开下载链接
            window.open(downloadLink, "_blank");
          })
          .catch((error) => {
            console.error("处理错误:", error);
          });

        // window.open("https://www.baidu.com", "_blank");
      }
    });

    console.log('成功添加"使用Trae国际版打开"菜单项');
  }

  // 3. 监听页面中semi-dropdown元素的出现（支持动态加载的元素）
  function observeSemiDropdown() {
    // 配置MutationObserver：监听子节点变化和属性变化
    const observerConfig = {
      childList: true, // 监听子节点增减
      subtree: true, // 监听后代节点（深层DOM）
      attributes: false, // 无需监听属性变化（可根据需求开启）
      characterData: false,
    };

    // 创建观察者实例
    const observer = new MutationObserver((mutations) => {
      // 遍历所有DOM变化
      mutations.forEach((mutation) => {
        // 情况1：子节点新增时，检查是否有semi-dropdown元素
        if (mutation.addedNodes.length) {
          mutation.addedNodes.forEach((node) => {
            // 只处理元素节点（排除文本节点、注释等）
            if (node.nodeType === 1) {
              // 检查当前节点是否是目标dropdown
              if (node.classList.contains("semi-dropdown")) {
                addTraeMenuItem(node);
              }
              // 检查当前节点的后代是否有目标dropdown（深层DOM）
              const dropdownsInNode = node.querySelectorAll(".semi-dropdown");
              dropdownsInNode.forEach(addTraeMenuItem);
            }
          });
        }
      });
    });

    // 启动观察者，监听整个文档（可根据需求缩小监听范围，如#app容器）
    observer.observe(document.body, observerConfig);
    console.log("已启动semi-dropdown监听");

    // 初始检查：页面加载完成后，检查已存在的semi-dropdown元素
    const existingDropdowns = document.querySelectorAll(".semi-dropdown");
    existingDropdowns.forEach(addTraeMenuItem);
  }

  // --------------------------
  // 自定义处理函数示例
  // --------------------------

  /**
   * 保存artifacts信息到localStorage
   */
  function saveArtifactsToLocalStorage(responseData) {
    if (responseData && responseData.artifacts) {
      try {
        localStorage.setItem(
          "artifacts",
          JSON.stringify(responseData.artifacts)
        );
        console.log("[监听器] artifacts信息已保存到localStorage");
        return true;
      } catch (error) {
        console.error("[监听器] 保存artifacts到localStorage失败:", error);
        return false;
      }
    } else {
      console.warn("[监听器] 响应数据中未找到artifacts信息");
      return false;
    }
  }

  /**
   * 将对象转换为URL参数字符串
   * @param {Object} obj - 要转换的对象
   * @returns {string} - 转换后的URL参数字符串
   */
  function objToParams(obj) {
    const params = new URLSearchParams();

    for (const key in obj) {
      // 处理数组类型的参数
      if (Array.isArray(obj[key])) {
        obj[key].forEach((value) => {
          params.append(key, value);
        });
      } else {
        params.append(key, obj[key]);
      }
    }

    return params.toString();
  }

  /**
   * 获取资源下载链接
   * @param {string} reqParams - 请求参数，格式为URL参数字符串
   * @param {Array} articleList - 文章列表，用于匹配资源下载链接
   * @returns {Promise} 返回一个Promise，包含下载链接数据
   */
  function getResourceDownloadUrl(reqParams, articleList) {
    const url =
      "https://www.doubao.com/samantha/code/get_resource_download_url?" +
      reqParams;

    const headers = {
      accept: "application/json, text/plain, */*",
      "accept-language": "zh-CN,zh;q=0.9",
      "agw-js-conv": "str",
      "content-type": "application/json",
      origin: "https://www.doubao.com",
      priority: "u=1, i",
      referer: window.location.href,
      "sec-ch-ua":
        '"Chromium";v="140", "Not=A?Brand";v="24", "Google Chrome";v="140"',
      "sec-ch-ua-mobile": "?0",
      "sec-ch-ua-platform": '"Windows"',
      "sec-fetch-dest": "empty",
      "sec-fetch-mode": "cors",
      "sec-fetch-site": "same-origin",
      "user-agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
    };

    // const body = {
    //   type: 3,
    //   artifacts_identifier: {
    //     code_id: "20378744161911554",
    //     code_version: 7,
    //     title: "wabi-sabi-startpage-3x3",
    //   },
    // };
    const body = {
      type: 3,
      artifacts_identifier: {
        code_id: articleList["artifacts"][0]["resource_id"],
        code_version: articleList["artifacts"][0]["resource_version"],
        title: articleList["artifacts"][0]["artifact_id"],
      },
    };

    return fetch(url, {
      method: "POST",
      headers: headers,
      credentials: "include", // 携带当前页面的Cookie
      body: JSON.stringify(body),
    })
      .then((response) => response.json())
      .then((data) => {
        console.log("获取下载链接结果:", data);
        return data; // 返回数据，这样调用者可以获取到结果
      })
      .catch((error) => {
        console.error("获取下载链接出错:", error);
        throw error; // 重新抛出错误，让调用者可以处理
      });
  }

  // --------------------------
  // 监听fetch请求
  // --------------------------
  const originalFetch = window.fetch;
  window.fetch = async function (input, init) {
    try {
      // 获取请求URL
      const url = input instanceof Request ? input.url : input;
      const method = (init?.method || input.method || "GET").toUpperCase();

      // 发送请求并获取响应
      const response = await originalFetch(input, init);

      // 克隆响应以便多次读取
      const clonedResponse = response.clone();

      // 尝试解析响应内容
      let responseBody;
      try {
        const contentType = clonedResponse.headers.get("content-type");
        if (contentType && contentType.includes("application/json")) {
          responseBody = await clonedResponse.json();
        } else if (contentType && contentType.includes("text/")) {
          responseBody = await clonedResponse.text();
        } else {
          responseBody = `[二进制数据，长度: ${
            clonedResponse.headers.get("content-length") || "未知"
          }]`;
        }
      } catch (e) {
        responseBody = `[解析响应失败: ${e.message}]`;
      }

      // 构建请求信息对象
      const requestInfo = {
        type: "fetch",
        url: url,
        method: method,
        status: response.status,
        statusText: response.statusText,
        response: responseBody,
        timestamp: new Date().toISOString(),
      };

      // 处理请求信息
      processRequest(requestInfo);

      return response;
    } catch (error) {
      console.error("[监听器] fetch请求出错:", error);
      throw error;
    }
  };

  // --------------------------
  // 监听XMLHttpRequest请求
  // --------------------------
  const originalXHROpen = XMLHttpRequest.prototype.open;
  XMLHttpRequest.prototype.open = function (method, url) {
    // 保存请求信息
    this._requestMethod = method.toUpperCase();
    this._requestUrl = url;
    this._startTime = new Date().toISOString();

    // 监听请求完成事件
    this.addEventListener("load", function () {
      let responseBody;
      try {
        // 尝试解析JSON响应
        if (this.responseText) {
          responseBody = JSON.parse(this.responseText);
        } else {
          responseBody = null;
        }
      } catch (e) {
        // 解析失败则直接使用文本
        responseBody =
          this.responseText || `[无响应内容，状态: ${this.status}]`;
      }

      // 构建请求信息对象
      const requestInfo = {
        type: "XMLHttpRequest",
        url: this._requestUrl,
        method: this._requestMethod,
        status: this.status,
        statusText: this.statusText,
        response: responseBody,
        timestamp: this._startTime,
      };

      // 处理请求信息
      processRequest(requestInfo);
    });

    // 监听错误事件
    this.addEventListener("error", function () {
      console.error(
        `[监听器] XHR请求错误: ${this._requestMethod} ${this._requestUrl}`
      );
    });

    // 调用原始的open方法
    return originalXHROpen.apply(this, arguments);
  };

  console.log("[监听器] 增强版请求监听器已启动");
  console.log(
    "[监听器] 正在监听的URL规则:",
    config.watchRules.map((r) => r.urlContains)
  );

  // --------------------------
  // 页面加载监听
  // --------------------------
  if (
    document.readyState === "complete" ||
    document.readyState === "interactive"
  ) {
    observeSemiDropdown();
  } else {
    // 若文档未就绪，监听DOMContentLoaded事件后执行
    document.addEventListener("DOMContentLoaded", observeSemiDropdown);
  }
})();
